package com.example.interceptor;

import basic.framework.components.mybatis.common.utils.ClassUtils;
import basic.framework.components.mybatis.common.utils.ReflectionUtils;
import basic.framework.components.mybatis.executor.criteria.Criteria;
import basic.framework.components.mybatis.executor.pagesort.PageSortHelper;
import com.mybatis.criteria.MyCriteria;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.*;

import static basic.framework.components.mybatis.executor.intercetor.BaseInterceptor.Index.PARAMETER_INDEX;

@Intercepts(@Signature(type = Executor.class,method = "query",args = {MappedStatement.class,Object.class,
        RowBounds.class, ResultHandler.class}))
public class MyWhereInteceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        final BoundSql boundSql = ms.getBoundSql(parameter);
        MyCriteria criteria = ClassUtils.findObjectFromParams(parameter, MyCriteria.class);
        //criteria = loadEntity(parameter, args, criteria); 不知道干嘛用的
        if (criteria != null && criteria.getSqlSegment() != null) {
            String sql = null;
            if (criteria.getContain()) {
                sql = String.format(" select t.* from ( %s ) t where  %s", boundSql.getSql(), criteria.toString());
            } else {
                sql = String.format("%s AND %s", boundSql.getSql(), criteria.toString());
            }
            loadCriteriaParams(ms, boundSql, args, criteria);//改变boudnsql的参数，先注释看看有没有影响
//            if (criteria.getSort() != null) {
//                sql = SqlUtils.applySorting(sql, criteria.getSort());
//
//            }
            args[0] = PageSortHelper.copyFromNewSql(ms, boundSql, sql);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
            return Plugin.wrap(target,this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    private void loadCriteriaParams(MappedStatement ms, BoundSql boundSql, Object[] args, MyCriteria criteria) {

        List<ParameterMapping> parameterMappingList = new ArrayList<>();
        parameterMappingList.addAll(boundSql.getParameterMappings());
        MapperMethod.ParamMap paramMap = null;
        Object parameter = args[PARAMETER_INDEX.getAnInt()];
        if (parameter instanceof MyCriteria) {
            Map map = new HashMap();
            map.put(Criteria.DEFAULT_PARAM_ALIAS, criteria.getParamNameValuePairs());
            args[PARAMETER_INDEX.getAnInt()] = map;
            paramMap = new MapperMethod.ParamMap();
        } else if (parameter instanceof MapperMethod.ParamMap) {
            paramMap = (MapperMethod.ParamMap) parameter;
            paramMap.put(Criteria.DEFAULT_PARAM_ALIAS, criteria.getParamNameValuePairs());
        } else if (parameter instanceof Map) {
            ((Map) parameter).put(Criteria.DEFAULT_PARAM_ALIAS, criteria.getParamNameValuePairs());
        }
        Map<String, Object> paramNameValuePairs = criteria.getParamNameValuePairs();
        for (Map.Entry<String, Object> stringObjectEntry : paramNameValuePairs.entrySet()) {
            ParameterMapping parameterMapping = new ParameterMapping.Builder(ms.getConfiguration(),
                    Criteria.DEFAULT_PARAM_ALIAS + "." + stringObjectEntry.getKey(),
                    stringObjectEntry.getValue() == null ? String.class : stringObjectEntry.getValue().getClass()).build();
            parameterMappingList.add(parameterMapping);
        }
        ReflectionUtils.setFieldValue(boundSql, "parameterMappings", parameterMappingList);
        ReflectionUtils.setFieldValue(boundSql, "parameterObject", args[1]);//这里设置成null了，改成args1看看


    }
}
